/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef UGAME_UTIL_CONFIGURATION_H_
#define UGAME_UTIL_CONFIGURATION_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cstdint>
#include <cstring>
#include <boost/unordered_map.hpp>
#include <UGame/Macros.h>
#include <UGame/Exception.h>

namespace UGame {
namespace Util {

    class Configuration
    {
    public:
        struct Any;
        typedef boost::unordered_map<std::string, struct Any>   ValueMap;

        struct Any {
            std::string Key;
            std::string Value;
            ValueMap Map;

			const struct Any* Get(const std::string& _Key) const
			{
				ValueMap::const_iterator _Iter = Map.find(_Key);
				if (_Iter != Map.end()) {
					return &(_Iter->second);
				}
				return NULL;
			}

			const struct Any& operator[](const std::string& _Key) const { 
				const struct Any* _Any = Get(_Key); 
				if (_Any != NULL) {
					return *_Any;
				}
				throw Exception(std::string("no such key \"").append(_Key).append("\""));
			}

			template<typename T>
			T As(void) const;
        };


        Configuration(void) : _errorString() {}
        ~Configuration(void){}

        virtual bool Open(const std::string& _File) = 0;
		virtual const struct Any* Get(const std::string& _Key) const = 0;

		const struct Any& operator[](const std::string& _Key) const {
			const struct Any* _Any = Get(_Key);
			if (_Any != NULL) {
				return *_Any;
			}
			throw Exception(std::string("no such key \"").append(_Key).append("\""));
		}

    protected:
        std::string _errorString;
    private:
        DISALLOW_COPY_AND_ASSIGN(Configuration);
    };

	template<typename T> inline T _Cast(const struct Configuration::Any* _Any) { return static_cast<T>(atol(_Any->Value.c_str())); }

	template<> inline std::int8_t Configuration::Any::As(void) const { return _Cast<std::int8_t>(this); }
	template<> inline std::uint8_t Configuration::Any::As(void) const { return _Cast<std::uint8_t>(this); }
	template<> inline std::int16_t Configuration::Any::As(void) const { return _Cast<std::int16_t>(this); }
	template<> inline std::uint16_t Configuration::Any::As(void) const { return _Cast<std::uint16_t>(this); }
	template<> inline std::int32_t Configuration::Any::As(void) const { return _Cast<std::int32_t>(this); }
	template<> inline std::uint32_t Configuration::Any::As(void) const { return _Cast<std::uint32_t>(this); }
	template<> inline std::int64_t Configuration::Any::As(void) const { return _Cast<std::int64_t>(this); }
	template<> inline std::uint64_t Configuration::Any::As(void) const { return _Cast<std::uint64_t>(this); }
	template<> inline double Configuration::Any::As(void) const { return _Cast<double>(this); }
	template<> inline std::string Configuration::Any::As(void) const { return Value; }
	template<> inline bool Configuration::Any::As(void) const { 
		if (Value.empty()) {
			return false;
		}
		return _Cast<std::int8_t>(this) != 0;
	}
}}

#endif // UGAME_UTIL_CONFIGURATION_H_
